package com.jetgege.resumepoint;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Created by JJT-ssd on 2017/4/20.
 */

public class DownloadManager {
    private static final AtomicReference<DownloadManager> INSTANCE=new AtomicReference<>();
    private HashMap<String, Call> downCalls;//用来存放各个下载的请求
    private OkHttpClient mClient;

    public static DownloadManager getInstance()
    {
        for (;;){
            DownloadManager current =INSTANCE.get();
            if (current!=null)return current;
            current=new DownloadManager();
            if (INSTANCE.compareAndSet(null,current))return current;
        }
    }

    private DownloadManager(){
        downCalls=new HashMap<>();
        mClient =new OkHttpClient.Builder().build();
    }

    public void download(String url,DownLoadObserver downLoadObserver){
        Observable.just(url)
                .filter(s->!downCalls.containsKey(s))//call的map已经有了，就证明正在下载，则这次不下载
                .flatMap(s->Observable.just(createDownInfo(s)))
                .map(this::getRealFileName)//检测本地文件夹，生成新的文件名
                .flatMap(downloadInfo -> Observable.create(new DownloadSubscribe(downloadInfo)))//下载
                .observeOn(AndroidSchedulers.mainThread())//在主线程回调
                .subscribeOn(Schedulers.io())//在子线程执行
                .subscribe(downLoadObserver);//添加观察者
    }

    private DownloadInfo getRealFileName(DownloadInfo downloadInfo) {

        String fileName =downloadInfo.getFileName();
        long downloadLength=0,contentLength =downloadInfo.getTotal();
        File file = new File(MyApp.sContext.getFilesDir(),fileName);
        if (file.exists()){
            //找到了文件，表示已经下载过，则获取其长度
            downloadLength =file.length();
        }
        //之前下载过，需要重新来一个文件
        int i = 1;
        while (downloadLength>=contentLength){
            int dotIndex = fileName.lastIndexOf(".");
            String fileNameOther;
            if (dotIndex==-1){
                fileNameOther =fileName+"("+i+")";
            }else {
                fileNameOther =fileName.substring(0,dotIndex)+"("+i+")"+fileName.substring(dotIndex);
            }
            File newFile =new File(MyApp.sContext.getFilesDir(),fileNameOther);
            file = newFile;
            downloadLength =newFile.length();
            i++;
        }
        //设置改变过的文件名/大小
        downloadInfo.setProgress(downloadLength);
        downloadInfo.setFileName(file.getName());
        return downloadInfo;
    }

    /**
     * 创建DownInfo
     * @param url 请求网址
     * @return
     */
    private DownloadInfo  createDownInfo(String url) {

        DownloadInfo downloadInfo = new DownloadInfo(url);
        long contentLength = getContentLength(url);//获得文件大小
        downloadInfo.setTotal(contentLength);
        String fileName = url.substring(url.lastIndexOf("/"));
        downloadInfo.setFileName(fileName);
        return downloadInfo;
    }


    /**
     * 获取下载文件的长度
     * @param  downloadUrl 下载链接
     * @return
     */
    private long getContentLength(String downloadUrl) {
        Request request =new Request.Builder()
                .url(downloadUrl)
                .build();
        try {
            Response response = mClient.newCall(request).execute();
            if (response!=null&&response.isSuccessful()){
                long contentLength=response.body().contentLength();
                response.close();
                return contentLength==0?DownloadInfo.TOTAL_ERROR:contentLength;

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return DownloadInfo.TOTAL_ERROR;
    }


    public class DownloadSubscribe implements ObservableOnSubscribe<DownloadInfo> {

        private DownloadInfo downloadInfo;
        public DownloadSubscribe(DownloadInfo downloadInfo){
            this.downloadInfo=downloadInfo;
        }
        @Override
        public void subscribe(ObservableEmitter<DownloadInfo> e) throws Exception {
            String url = downloadInfo.getUrl();
            long downloadLength =downloadInfo.getProgress();//已经下载好的长度
            long contentLength = downloadInfo.getTotal();//文件的总长度
            //初始进度信息
            e.onNext(downloadInfo);

            Request request = new Request.Builder()
                    .addHeader("RANGE","bytes=" + downloadLength+"-"+contentLength)
                    .url(url)
                    .build();
            Call call = mClient.newCall(request);
            downCalls.put(url,call);//
            Response response = call.execute();
            File file = new File(MyApp.sContext.getFilesDir(),downloadInfo.getFileName());
            InputStream is = null;
            FileOutputStream fileOutputStream = null;
            try{
                is = response.body().byteStream();
                fileOutputStream=new FileOutputStream(file,true);
                byte[] buffer = new byte[2048];//缓冲数组2kB
                int len;
                while ((len=is.read(buffer))!=-1){
                    fileOutputStream.write(buffer,0,len);
                    downloadLength+=len;
                    downloadInfo.setProgress(downloadLength);
                    e.onNext(downloadInfo);
                }
                fileOutputStream.flush();
                downCalls.remove(url);
            }finally {
                //关闭IO流
                IOUtil.closeAll(is,fileOutputStream);
            }
            e.onComplete();//完成
        }
    }

    public void cancel(String url){
        Call call =downCalls.get(url);
        if (call!=null){
            call.cancel();//取消
        }
        downCalls.remove(url);
    }

}
